#ifndef __LEARNOPENG_CSCENEMANAGER_H_INCLUDE__
#define __LEARNOPENG_CSCENEMANAGER_H_INCLUDE__
#include "LDXConfig.h"
#include "CTexture.h"
#include "ISceneNodeAnimator.h"
#include "ICameraSceneNode.h"
#include "SKeyMap.h"
#include "SColorf.h"
#include "S3DVertex.h"
#include "DCBufferDefines.h"
#include "SDeviceMap.h"


namespace ldx
{
	namespace device
	{
		class WinDevice;
		class CCursorControl;
	}

	namespace render
	{
		class IRenderDriver;
		class CShaderManager;
		class CBufferObject;
	}

	namespace scene
	{
		class CLightsManager;
		class ISceneNode;
		class ITerrainSceneNode;
		class IMesh;
		class IAnimatedMesh;
		class ICameraSceneNode;
		class IMeshLoader;
		class IMeshCache;
		class IMeshBuffer;
		class CLightSceneNode;
		enum E_LIGHT_MOVE_TYPE;
		//! Enumeration for render passes.
		/** A parameter passed to the registerNodeForRendering() method of the ISceneManager,
		specifying when the node wants to be drawn in relation to the other nodes. */
		enum E_SCENE_NODE_RENDER_PASS
		{
			//! No pass currently active
			ESNRP_NONE = 0,

			//! Camera pass. The active view is set up here. The very first pass.
			ESNRP_CAMERA = 1,

			//! 
			ESNRP_LIGHT = 2,

			//! This is used for sky boxes.
			ESNRP_SKY_BOX = 4,

			//! All normal objects can use this for registering themselves.
			/** This value will never be returned by
			ISceneManager::getSceneNodeRenderPass(). The scene manager
			will determine by itself if an object is transparent or solid
			and register the object as SNRT_TRANSPARENT or SNRT_SOLD
			automatically if you call registerNodeForRendering with this
			value (which is default). Note that it will register the node
			only as ONE type. If your scene node has both solid and
			transparent material types register it twice (one time as
			SNRT_SOLID, the other time as SNRT_TRANSPARENT) and in the
			render() method call getSceneNodeRenderPass() to find out the
			current render pass and render only the corresponding parts of
			the node. */
			ESNRP_AUTOMATIC = 24,

			//! Solid scene nodes or special scene nodes without materials.
			ESNRP_SOLID = 8,

			//! Will use tesc tse.
			ESNRP_TERRAIN_LIKE = 16,

			//! Drawn after the solid nodes, before the transparent nodes, the time for drawing shadow volumes
			ESNRP_SHADOW = 64
		};

		class CSceneManager :public ISceneNode
		{
		public:

			CSceneManager(device::WinDevice* device);

			~CSceneManager();

			virtual void drawAll();

			CLightsManager* getLightsManager();


			render::IRenderDriver * getRenderDriver();

			bool setActiveCamera(ICameraSceneNode* camera);

			ICameraSceneNode* getActiveCamera();

			//! registers a node for rendering it at a specific time.
			virtual uint registerNodeForRendering(ISceneNode* node, E_SCENE_NODE_RENDER_PASS pass = ESNRP_AUTOMATIC);

#pragma region addAssets

			virtual IAnimatedMesh* getMesh(const std::string& filename);

			//! AddMesh bu SAnimatedMesh.
			virtual bool addMesh(const std::string& name, IAnimatedMesh* mesh);

			//! AddMesh bu SMesh,will be pack as animated mesh in cache.
			virtual bool addMesh(const std::string& name, IMesh* mesh);

			//! AddMesh bu IMeshBuffer,will be packed as smesh then animatedmesh.
			virtual bool addMesh(const std::string& name, IMeshBuffer* buffer);

			virtual IMeshCache* getMeshCache();		

#pragma endregion


#pragma region addscenenodes
			// !Adds a camera scene node to the tree and sets it as active camera.
			//! \param position: Position of the space relative to its parent where the camera will be placed.
			//! \param lookat: Position where the camera will look at. Also known as target.
			//! \param parent: Parent scene node of the camera. Can be null. If the parent moves,
			//! the camera will move too.
			//! \return Pointer to interface to camera
			virtual ICameraSceneNode* addCameraSceneNode(ISceneNode* parent = 0,
				const mt::vec3f& position = mt::vec3f(0, 0, 0),
				const mt::vec3f& lookat = mt::vec3f(0, 0, 100),
				int id = -1, bool makeActive = true);

			//! Adds a camera scene node which is able to be controled with the mouse and keys
			//! like in most first person shooters (FPS):
			virtual ICameraSceneNode* addCameraSceneNodeFPS(ISceneNode* parent = 0,
				float rotateSpeed = 100.0f, float moveSpeed = 20.f, int id = -1,
				SKeyMap* keyMapArray = 0, int keyMapSize = 0,
				bool noVerticalMovement = false, float jumpSpeed = 0.f,
				bool invertMouseY = false, bool makeActive = true);

			//! Adds a dynamic light scene node. The light will cast dynamic light on all
			//! other scene nodes in the scene, which have the material flag video::MTF_LIGHTING
			//! turned on. (This is the default setting in most scene nodes).
			/*virtual ILightSceneNode* addLightSceneNode(ISceneNode* parent = 0,
			const core::vector3df& position = core::vector3df(0, 0, 0),
			video::SColorf color = video::SColorf(1.0f, 1.0f, 1.0f),
			float range = 100.0f, int id = -1);*/

			virtual ISceneNode* addModelSceneNode(
				std::string modelPath,
				ISceneNode* parent,
				int id = -1,
				const mt::vec3f& position = mt::vec3f(0, 0, 0),
				const mt::vec3f& rotation = mt::vec3f(0, 0, 0),
				const mt::vec3f& scale = mt::vec3f(1.0f, 1.0f, 1.0f));

			virtual ISceneNode* addCubeSceneNode(ISceneNode* parent = 0,
				int id = -1,std::string tex = "",
				const mt::vec3f& position = mt::vec3f(0, 0, 0),
				const mt::vec3f& rotation = mt::vec3f(0, 0, 0),
				const mt::vec3f& scale = mt::vec3f(1.0f, 1.0f, 1.0f));

			CLightSceneNode* addLight(
				E_LIGHT_MOVE_TYPE type,
				ISceneNode* parent,
				int id = -1,
				mt::vec4f position = mt::vec4f(0, 0, 0, 1),
				mt::vec3f intensities = mt::vec3f(1, 1, 1),
				float attenuation = 0.f,
				float ambientCoefficient = 0.01f,
				float coneAngle = 180.f,
				mt::vec3f coneDirection = mt::vec3f(0, 0, 1));

			//! Build spot light
			CLightSceneNode* addSpotLight(
				E_LIGHT_MOVE_TYPE type,
				ISceneNode* parent,
				int id = -1,
				mt::vec4f position = mt::vec4f(-1, -1, -1, 1),
				mt::vec3f  intensities = mt::vec3f(2, 2, 2),
				float attenuation = 0.1f,
				float ambientCoefficient = 0.06f);

			//! Build parallel light
			CLightSceneNode* addParalleLight(
				E_LIGHT_MOVE_TYPE type,
				ISceneNode* parent,
				int id = -1,
				mt::vec4f position = mt::vec4f(-1.f, -1.f, -1.f, 0.f),
				mt::vec3f  intensities = mt::vec3f(0.3f, 0.3f, 0.3f),
				float ambientCoefficient = 0.06f);

			//! Build flash light.
			CLightSceneNode* addFlashLight(
				E_LIGHT_MOVE_TYPE type,
				ISceneNode* parent,
				int id = -1,
				mt::vec4f position = mt::vec4f(-1, -1, -1, 1),
				mt::vec3f  intensities = mt::vec3f(2, 2, 2),
				float attenuation = 0.1f,
				float ambientCoefficient = 0.06f,
				float coneAngle = 15.0f,
				mt::vec3f coneDirection = mt::vec3f(0, 0, -1));
#pragma endregion


#pragma region addAnimators
			//! Creates a fly circle animator, which lets the attached scene node fly around a center.
			/** \param center: Center of the circle.
			\param radius: Radius of the circle.
			\param speed: The orbital speed, in radians per millisecond.
			\param direction: Specifies the upvector used for alignment of the mesh.
			\param startPosition: The position on the circle where the animator will
			begin. Value is in multiples of a circle, i.e. 0.5 is half way around. (phase)
			\param radiusEllipsoid: if radiusEllipsoid != 0 then radius2 froms a ellipsoid
			begin. Value is in multiples of a circle, i.e. 0.5 is half way around. (phase)
			\return The animator. Attach it to a scene node with ISceneNode::addAnimator()
			and the animator will animate it.
			If you no longer need the animator, you should call ISceneNodeAnimator::drop().
			See IReferenceCounted::drop() for more information. */
			virtual ISceneNodeAnimator* createFlyCircleAnimator(
				const mt::vec3f& center = mt::vec3f(0.f, 0.f, 0.f),
				float radius = 100.f, float speed = 0.1f,/*in ms*/
				const mt::vec3f& direction = mt::vec3f(0.f, 1.f, 0.f),
				float startPosition = 0.f,
				float radiusEllipsoid = 0.f);


#pragma endregion

			//! Posts an input event to the environment. Usually you do not have to
			//! use this method, it is used by the internal engine.
			bool postEventFromUser(const SEvent& event);

			//! Set Shader's buffer
			bool setGlobalConstantBuffers(uint types ,
				mt::uint vsstartSlot=  0,
				mt::uint psstartSlot = 0, mt::uint gsstartSlot = 0);

		private:

			bool init();
			//! init global buffers.
			void initLocalBuffers();
			//! update global buffers.
			void updateLocalBuffers();
			device::WinDevice* Device;
			CLightsManager* LightsMgr;
			render::IRenderDriver * RenderDriver;
			device::CCursorControl* CursorControl;
			// hold
			render::CShaderManager* ShaderManager;
		
			std::vector<ISceneNode*> CameraList;
			std::vector<ISceneNode*> SkyBoxList;
			std::vector<ISceneNode* > SolidNodeList;

			std::vector<IMeshLoader*> MeshLoadersList;
			//! Mesh cache
			IMeshCache* MeshCache;

			ICameraSceneNode* CurrentCamera;
			mt::vec3f CamWorldPosition;

			E_SCENE_NODE_RENDER_PASS CurrentRendertime;

			render::CBufferCamera BufferCamera;
			render::CBufferObject* BufferCameraObj;
		};
	}

}

#endif//! __LEARNOPENG_CSCENEMANAGER_H_INCLUDE__